Zarządzanie adresami URL
========================

Pełne zarządzanie adresami URL w ramach aplikacji webowej składa się z dwóch
aspektów. Pierwszy, gdy ze strony użytkownika pojawia się żądanie w formacie
URL. Wówczas aplikacja musi przetworzyć je do postaci zrozumiałych dla siebie
parametrów. W drugim przypadku aplikacja musi dostarczyć mechanizmu tworzenia
takich adresów URL, by były one zrozumiałe dla samej aplikacji.
W przypadku aplikacji Yii jest to dokonywane przy pomocy [CUrlManager].

Tworzenie adresów URL
---------------------

Pomimo, że adresy URL mogą być sztywno zapisane w widokach kontrolera,
bardziej elastycznym sposobem jest ich dynamiczne tworzenie:

~~~
[php]
$url=$this->createUrl($route,$params);
~~~

gdzie `$this` odnosi się do instancji kontrolera; `$route` określa trasę
[route](/doc/guide/basics.controller#route) wywołania; `$params` jest listą
parametrów `GET`, dodaną do adresu URL.

Domyślnie adres URL utworzony przez [createUrl|CController::createUrl]
jest w tak zwanym formacie `get`. Przykładowo, dla zadanych `$route='post/read'`
i `$params=array('id'=>100)`, uzyskamy następujący URL:

~~~
/index.php?r=post/read&id=100
~~~

parametry pojawiają się w wywołaniu w postaci listy wyrażeń `Name=Value`,
złączonych znakiem ampersand (&). Parametr `r` reprezentuje żądanie
[route](/doc/guide/basics.controller#route). Ten format URL nie jest zbyt
przyjazny użytkownikowi, ponieważ wymaga kilku nieczytelnych znaków.

Możemy sprawić by powyższy URL prezentował się czytelniej i był bardziej
zrozumiały używając adresu formatowanego ukośnikami (ang. `path`), który
eliminuje kwerendę i umieszcza parametry GET na ścieżce adresu URL:

~~~
/index.php/post/read/id/100
~~~

Aby zmienić format adresów URL musimy skonfigurować komponent aplikacji
[urlManager|CWebApplication::urlManager] tak, by [createUrl|CController::createUrl]
mógł automatycznie przełączyć się na nowy format i by aplikacja mogła zrozumieć
nowe adresy URL:

~~~
[php]
array(
	......
	'components'=>array(
		......
		'urlManager'=>array(
			'urlFormat'=>'path',
		),
	),
);
~~~

Zauważ, że nie musimy definiować klasy komponentu [urlManager|CWebApplication::urlManager]
ponieważ jest ona wstępnie zadeklarowana jako [CUrlManager] w [CWebApplication].

> Wskazówka: URL wygenerowany przy pomocy metody [createUrl|CController::createUrl]
jest adresem względnym. Aby uzyskać pełny adres wystarczy poprzedzić adres względny
`Yii::app()->request->hostInfo` lub wywołać metodę [createAbsoluteUrl|CController::createAbsoluteUrl].

Przyjazne adresy URL
--------------------

Gdy używany jest URL formatowany ukośnikami (ang. `path`) możemy zdefiniować
pewne reguły tworzenia URL tak, by adres był jeszcze bardziej przyjazny użytkownikowi.
Przykładowo możemy generować adres tak krótki jak `/post/100`, zamiast długiego
`/index.php/post/read/id/100`. Reguły tworzenia adresów URL używane są przez
[CUrlManager], zarówno do celów tworzenia, jak i przetwarzania adresów URL.

Aby utworzyć te reguły musimy skonfigurować właściwość [rules|CUrlManager::rules]
komponentu aplikacji [urlManager|CWebApplication::urlManager]:

~~~
[php]
array(
	......
	'components'=>array(
		......
		'urlManager'=>array(
			'urlFormat'=>'path',
			'rules'=>array(
				'pattern1'=>'route1',
				'pattern2'=>'route2',
				'pattern3'=>'route3',
			),
		),
	),
);
~~~

Opisywane reguły definiowane są w tablicy przechowującej pary wyrażeń postaci
wzorzec-trasa, każda z takich par odpowiada jednej regule. Wzorzec reguły jest łańcuchem
używanym w celu dopasowania części informacyjnej adresu URL do wzorca. 
[Trasa](/doc/guide/basics.controller#route) powinna odnosić się do poprawnej trasy kontrolera.

Poza powyższym formatem wzorzec-trasa, reguła może być również określona poprzez
dostosowanie opcji w następujący sposób:

~~~
[php]
'pattern1'=>array('route1', 'urlSuffix'=>'.xml', 'caseSensitive'=>false)
~~~

Poczynając od wersji 1.1.7, można używać następującego formatu ( wzorzec jest 
określony w postaci elementu tablicy ) co pozwala określić kilka reguł 
z tym samym wzorcem:

~~~
[php]
array('route1', 'pattern'=>'pattern1', 'urlSuffix'=>'.xml', 'caseSensitive'=>false)
~~~

W powyższym kodzie, tablica zawiera listę dodatkowych opcji dla reguły. 
Dostępne opcje zostały wyjaśnione poniżej:

   - [pattern|CUrlRule::pattern]: wzorzec używany do dopasowywania oraz tworzenia URLi. 
   Opcja ta jest dostępna od wersji 1.1.7

   - [urlSuffix|CUrlRule::urlSuffix]: sufiks adresu URL używany specjalnie dla danej reguły.  
   Domyślnie posiada wartość null, oznaczającą że używana jest wartość [CUrlManager::urlSuffix].

   - [caseSensitive|CUrlRule::caseSensitive]: określa czy reguła uwzględnia wielkość liter. Domyślnie 
   posiada wartość null, co oznacza, że używana jest wartość [CUrlManager::caseSensitive].

   - [defaultParams|CUrlRule::defaultParams]: domyślne parametry GET (`nazwa=>wartość`), które ustanawia 
   dana reguła. Kiedy dana reguła używana jest do parsowania przychodzącego żądania, wartości zadeklarowane
   w tej właściwości zostaną wstrzyknięte do `$_GET`.

   - [matchValue|CUrlRule::matchValue]: określa czy wartości parametru GET powinny pasować do odpowiadających 
   im podwzorców w regule, kiedy tworzony jest URL. Domyślnie przyjmuje wartość null, oznaczającą używanie wartości
   [CUrlManager::matchValue]. Jeśli wartość ta wynosi false, oznacza to, że reguła będzie używana do tworzenia URL 
   jeśli jej trasa i nazwy parametrów są zgodnie z podanymi. Jeśli właściwość ta przyjmuje wartość true, wtedy 
   podane wartości parametru muszą również pasować do odpowiadających parametrowi podwzorców. Zauważ, że 
   ustawienie tej pozycji na true może obniżyć wydajność.
   
   - [verb|CUrlRule::verb]: metody HTTP (np. `GET`, `POST`, `DELETE`), do których ta reguła ma być dopasowana
   w celu używania jej do analizowania aktualnego żądania. Domyślnie null, co oznacza, że reguła jest dopasowywana
   do każdej metody HTTP. Jeżeli reguła ma być dopasowana do wielu metod, ich nazwy muszą być rozdzielone przecinkami. 
   Jeżeli reguła nie jest dopasowana do określonej metody (metod), zostanie ona pominięta podczas analizowania żądania.
   Opcja ta jest używana jedynie do analizowania żądania i dostarczona jest głównie w celu wsparcia RESTful URL. 
   Opcja ta jest dostępna od wersji 1.1.7.

   - [parsingOnly|CUrlRule::parsingOnly]: określa czy reguła jest używana jedynie do analizowania żądania.
   Domyślnie przyjmuje wartość false, co oznacza, że reguła jest używana zarówno do analizowania adresów URL jak i ich tworzenia.
   Opcja ta jest dostępna od wersji 1.1.7.

Używanie nazwanych parametrów
-----------------------------

Reguła może być powiązana z kilkoma parametrami GET. Te parametry pojawiają
się we wzorcu reguły jako specjalne znaczniki o formacie:

~~~
<ParamName:ParamPattern>
~~~

gdzie `ParamName` określa nazwe parametru GET, a opcjonalny `ParamPattern` definiuje
wyrażenie regularne, które ma być używane do badania dopasowania wartości parametru
GET. W przypadku gdy pominięto `ParamPattern`, oznacza to, że parametr ten może zawierać
wszystkie znaki poza ukośnikiem `/`. Gdy tworzymy adres URL znaczniki tych parametrów 
zostaną zastąpione odpowiednimi wartościami parametru GET; podczas przetwarzania 
adresu URL odpowiednie parametry GET zostaną wypełnione wynikami tego przetwarzania.

Pokażmy kilka przykładów by wyjaśnić jak działają reguły adresów URL. Zakładamy,
że nasz zestaw reguł składa się z trzech, widocznych poniżej:

~~~
[php]
array(
	'posts'=>'post/list',
	'post/<id:\d+>'=>'post/read',
	'post/<year:\d{4}>/<title>'=>'post/read',
)
~~~

   - Wywołanie `$this->createUrl('post/list')` tworzy `/index.php/posts`.
Stosowana jest pierwsza reguła.

   - Wywołanie `$this->createUrl('post/read',array('id'=>100))` tworzy
`/index.php/post/100`. Zastosowana została druga reguła.

   - Wywołanie `$this->createUrl('post/read',array('year'=>2008,'title'=>'a
sample post'))` tworzy `/index.php/post/2008/a%20sample%20post`. Zastosowana
została trzecia reguła.

   - Wywołanie `$this->createUrl('post/read')` tworzy
`/index.php/post/read`. Żadna z reguł nie została użyta.

Podsumowując: gdy używamy [createUrl|CController::createUrl] do generowania
adresów URL, trasa i parametry GET przekazywane do tej metody umożliwiają
wybór reguły, która ma być zastosowana. Jeżeli każdy z parametrów powiązanych
z pewną reguła występuje również wśród parametrów GET przekazywanych do
[createUrl|CController::createUrl] i jeżeli trasa zawarta w tej regule pasuje
do trasy w parametrach wywołania metody, to ta reguła będzie użyta
do wygenerowania adresu URL.

Jeżeli parametrów GET przekazywanych do metody [createUrl|CController::createUrl]
jest więcej niż wymaga jakakolwiek reguła, nadmiarowe parametry pojawią się
w ciągu argumentów tej metody. Przykładowo: jeżeli wywołamy
`$this->createUrl('post/read',array('id'=>100,'year'=>2008))` otrzymalibyśmy
`/index.php/post/100?year=2008`. Po to, by te dodatkowe parametry pojawiły się
w części informacyjnej adresu, powinniśmy dodać `/*` do reguły. Wówczas, używając
reguły `post/<id:\d+>/*`, możemy uzyskać adres URL postaci
`/index.php/post/100/year/2008`.

Jak wcześniej wspominaliśmy innym zastosowaniem reguł URL jest przetwarzanie
wywoływanych adresów URL. Jest to oczywiście proces odwrotny do tworzenia
adresów. Np. gdy użytkownik zgłasza żądanie adresu `/index.php/post/100`,
zastosowanie będzie miała druga reguła z przykładu wyżej. Spowoduje to
rozłożenie żądania na trasę `post/read` i parametr GET `array('id'=>100)`
(dostępny poprzez `$_GET`).

> Uwaga: korzystanie z reguł URL obniża wydajność aplikacji. Dzieje się tak
ponieważ [CUrlManager] przetwarzając wywoływany URL porównuje go z każdą regułą,
aż nie trafi na odpowiednią. Im większa ilość reguł, tym większy mają one wpływ na wydajność. 
Z tego względu w mocno obciążanych aplikacjach webowych należy minimalizować użycie reguł URL.

Parametryzowane trasy
---------------------

Możemy odnosić się do nazwanych parametrów w części reguły związanej z trasą. 
Pozwala to zastosować regułę do wielu tras w oparciu o spełniane kryterium.
Może to pomóc zredukować ilość reguł potrzebnych dla aplikacji i przez to zwiększyć
ogólną wydajność. 

Będziemy używać następujących przykładowych reguły aby zilustrować jak parametryzować
trasę za pomocą nazwanych parametrów:

~~~
[php]
array(
  '<_c:(post|comment)>/<id:\d+>/<_a:(create|update|delete)>' => '<_c>/<_a>',
  '<_c:(post|comment)>/<id:\d+>' => '<_c>/read',
  '<_c:(post|comment)>s' => '<_c>/list',
)
~~~

W powyższym kodzie, użyliśmy dwóch nazwanych parametrów w części reguły odnoszącej się 
do trasy: `_c` oraz `_a`. Pierwszy parametr odpowiada ID kontrolera posiadającym wartość `post` lub `comment`,
podczas gdy drugi odpowiada ID akcji, która może mieć wartość `create`, `update` lub `delete`.
Możesz nazywać parametry dowolnie, dopóki ich nazwy nie konfliktują z parametrami GET 
znajdującymi się w URLach.

Używając powyższych reguł, URL `/index.php/post/123/create` zostanie sparsowany na 
trasę `post/create` z parametrem GET `id=123`. A biorąc pod uwagę trasę 
`comment/list` oraz parametr GET `page=2`, możemy stworzyć URL
`/index.php/comments?page=2`.

Parametryzowanie nazw hostów
----------------------------

Możliwe jest dołączanie nazwy hosta do reguł parsowania 
oraz tworzenia URLi. Można wyodrębnić część nazwy hosta do parametru GET. Na przykład,
adres URL `http://admin.example.com/en/profile` może zostać sparsowany do parametrów 
GET `user=admin` oraz `lang=en`. Z drugiej strony, reguły z nazwą hosta mogą zostać użyte 
do utworzenia URLi z parametryzowanymi nazwami hostów.

W celu używania parametryzowanych nazw hostów, po prostu zadeklaruj regułę URL z 
informacją o hoście, np: 

~~~
[php]
array(
  'http://<user:\w+>.example.com/<lang:\w+>/profile' => 'user/profile',
)
~~~

Powyższy przykład mówi, iż pierwszy segment w nazwie hosta powinien być traktowany jako
parametr `user`, zaś pierwszy segment w ścieżce powinien być parametrem `lang`. 
Reguła odpowiada trasie `user/profile`.

Zauważ, że [CUrlManager::showScriptName] nie będzie działało, jeśli URL zostanie 
utworzony za pomocą reguły z parametryzowaną nazwą hosta.

Zauważ, że reguła ze sparametryzowaną nazwą hosta NIE POWINNA zawierać podkatalogu
jeśli aplikacja znajduje się w podkatalogu katalogu głównego www.
Na przykład, jeśli aplikacja znajduje się w 
`http://www.example.com/sandbox/blog`, wtedy wciąż powinniśmy używać tej samej reguły URL 
jak opisana powyżej bez podkatalogu `sandbox/blog`.

Ukrywanie `index.php`
---------------------

Czyszcząc adresy URL możemy zrobić jeszcze jedną rzecz więcej ukrywając
w adresie URL skrypt startowy `index.php`. To wymaga od nas skonfigurowania
web serwera oraz komponentu aplikacji [urlManager|CWebApplication::urlManager].

Po pierwsze musimy skonfigurować web serwer tak, by adres URL pozbawiony
skryptu wejściowego mógł być zawsze obsługiwany z uwzględnieniem skryptu.
W przypadku [serwera HTTP Apache](http://httpd.apache.org/) może to być zrealizowane poprzez
włączenie tzw. mechanizmu nadpisywania URL (ang. URL rewriting engine)
i zdefiniowanie kilku reguł nadpisywania. Możemy stworzyć plik `/wwwroot/blog/.htaccess` 
z następującą zawartością. Zauważ, że ta sama zawartość może zostać umieszczona 
w pliku konfiguracji Apache'a wewnątrz elementu `Directory` dla `/wwwroot/blog`.

~~~
RewriteEngine on

# jeżeli katalog lub plik istnieje użyj ich bezpośrednio
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

# w przeciwnym razie przekieruj na index.php
RewriteRule . index.php
~~~

Później konfigurujemy wspomnianą właściwość [showScriptName|CUrlManager::showScriptName]
komponentu [urlManager|CWebApplication::urlManager] przypisując jej wartość `false`.

Teraz możemy wywołać `$this->createUrl('post/read',array('id'=>100))`, otrzymując URL
`/post/100`. I co ważniejsze ten adres URL będzie poprawnie rozpoznany przez twoją
aplikację webową.

Pozorowany sufiks adresu URL
----------------------------

Możemy również dodawać pewne sufiksy do adresów URL. Na przykład możemy uzyskać
`/post/100.html` zamiast `/post/100`. To sprawia, że wygląda on bardziej jak URL
do strony statycznej. Aby to zrobić po prostu skonfiguruj komponent
[urlManager|CWebApplication::urlManager] ustawiając jego właściwość
[urlSuffix|CUrlManager::urlSuffix] na taki sufiks, jaki ci odpowiada.

Używanie własnych klas reguł URL
--------------------------------

> Note|Uwaga: Używanie własnych klas reguł URL jest wspierane od wersji 1.1.8.

Domyślnie, każda reguła URL zadeklarowana w [CUrlManager] jest reprezentowana jako 
obiekt [CUrlRule], który wykonuje zadanie przetwarzania żądań i tworzenia URLi
w oparciu o określone reguły. Chociaż klasa [CUrlRule] jest dostatecznie elastyczna aby
poradzić sobie z wszystkimi formatani URL, czasami wciąż chcemy ją rozszerzyć o dodatkowe
funkcjonalności.

Na przykład, na stronie dilera samochodów, chcemy wspierać format URL w postaci
`/Manufacturer/Model`, gdzie producent `Manufacturer` i model samochodu `Model` 
muszą zgadzać się z danymi znajdującymi się w bazie danych. 
Klasa [CUrlRule] nie zadziała, gdyż działa w oparciu o statycznie zadeklarowane wyrażenia
regularne, które nie mają wiedzy o bazie danych.

Możemy utworzyć nową klasę reguł URL poprzez rozszerzenie [CBaseUrlRule] i użycie jej 
w jednej lub wielu regułach URL. Używając powyższej strony dilera samochodowego jako przykładu,
możemy zadeklarować następujące reguły URL:

~~~
[php]
array(
	// standardowa reguła mapująca '/' do akcji 'site/index'
	'' => 'site/index',

	// standardowa reguła mapująca '/login' do 'site/login', i tak dalej...
	'<action:(login|logout|about)>' => 'site/<action>',

	// własna reguła obsługującca format '/Manufacturer/Model'
	array(
	    'class' => 'application.components.CarUrlRule',
	    'connectionID' => 'db',
	),

	// standardowa reguła obsługująca 'post/update', i tak dalej...
	'<controller:\w+>/<action:\w+>' => '<controller>/<action>',
),
~~~

W powyższym kodzie użyliśmy własnej klasy reguł URL `CarUrlRule` aby obsłużyć format URL
`/Manufacturer/Model`. Klasa ta może zostać napisana w następujący sposób:

~~~
[php]
class CarUrlRule extends CBaseUrlRule
{
	public $connectionID = 'db';

	public function createUrl($manager,$route,$params,$ampersand)
	{
		if ($route==='car/index')
		{
			if (isset($params['manufacturer'], $params['model']))
				return $params['manufacturer'] . '/' . $params['model'];
			else if (isset($params['manufacturer']))
				return $params['manufacturer'];
		}
		return false;  // this rule does not apply
	}

	public function parseUrl($manager,$request,$pathInfo,$rawPathInfo)
	{
		if (preg_match('%^(\w+)(/(\w+))?$%', $pathInfo, $matches))
		{
			// sprawdź $matches[1] oraz $matches[3] aby zobaczyć, czy
			// zawierają one producenta oraz model z bazy danych
			// Jeśli tak, to ustaw $_GET['manufacturer'] oraz $_GET['model']
			// i zwróć 'car/index'
		}
		return false;  // reguła ta nie ma zastosowania
	}
}
~~~

Własna klasa URL musi implementować dwie abstrakcyjne metody zadeklarowane w klasie [CBaseUrlRule]:

* [createUrl()|CBaseUrlRule::createUrl()]
* [parseUrl()|CBaseUrlRule::parseUrl()]

Poza powyższym, typowym użyciem, własne klasy reguł URL mogą być implementowane z innych powodów.
Na przykład, chcemy napisać klasę reguł logującą parsowanie URL i tworzenie żądania.
Może być to bardzo użyteczne na etapie rozwoju aplikacji. Możemy również napisać klasę reguły
do wyświetlania specjalnej strony błędu 404 w przypadku gdy wszystkie pozostałe reguły URL
nie będą potrafiły rozszyfrować aktualnego żądania. Zauważ, że w takim przypadku, reguła
z tą specjalną klasą musi zostać zadeklarowana jako ostatnia.

<div class="revision">$Id: topics.url.txt 3591 2012-02-17 21:44:32Z qiang.xue@gmail.com $</div>